﻿using Sidvall.Security;
using Sidvall.Web.IO.Data;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.Web.IO.Models
{
    public abstract class WebApiModelBase
    {
        #region Public Members

        protected abstract Task<ResourceItemCollection> GetResourceItemsAsync();

        public List<string> Resources { get; set; }
        public List<string> Actions { get; set; }
        public Sidvall.Security.ResourceItemCollection ResourceItems { get; set; }
        public List<StoreResourceItem> StoreResourceItems { get; set; }
        public ResourceResultCollection ResourceResults { get; set; }

        #region GetResult

        public virtual Sidvall.Security.ResourceItem GetResult(string resource, string action)
        {
            List<ResourceItem> items;

            if (this.ResourceResults != null)
                return this.ResourceResults.GetResourceItem(resource, action);

            switch (resource)
            {
                case Sidvall.IO.Security.DomainObjects.File:
                    if (!string.IsNullOrWhiteSpace(action))
                    {
                        switch (action)
                        {
                            case Sidvall.Security.Operations.DeleteItems:
                            case Sidvall.Security.Operations.GetItems:
                            case Sidvall.Security.Operations.GetItemsMetadata:
                            case Sidvall.Security.Operations.SaveItems:
                                break;
                            default:
                                return new ResourceItem()
                                {
                                    AccessLevel = ResourceItemAccessLevel.None,
                                };
                        }
                    }
                    items = GetResourceItems(this.StoreResourceItems, resource, action);
                    break;
                case Sidvall.IO.Security.DomainObjects.FileSystemEntry:
                    if (!string.IsNullOrWhiteSpace(action))
                    {
                        switch (action)
                        {
                            case Sidvall.Security.Operations.GetItemsMetadata:
                                break;
                            default:
                                return new ResourceItem()
                                {
                                    AccessLevel = ResourceItemAccessLevel.None,
                                };
                        }
                    }
                    items = GetResourceItems(this.StoreResourceItems, resource, action);
                    break;
                case Sidvall.IO.Security.DomainObjects.Folder:
                    if (!string.IsNullOrWhiteSpace(action))
                    {
                        switch (action)
                        {
                            case Sidvall.Security.Operations.AddItems:
                            case Sidvall.Security.Operations.DeleteItems:
                            case Sidvall.Security.Operations.GetItemsMetadata:
                            case Sidvall.Security.Operations.SaveItems:
                                break;
                            default:
                                return new ResourceItem()
                                {
                                    AccessLevel = ResourceItemAccessLevel.None,
                                };
                        }
                    }
                    items = GetResourceItems(this.StoreResourceItems, resource, action);
                    break;
                default:
                    items = GetResourceItems(this.ResourceItems, resource, action);
                    break;
            }
            return Sidvall.Security.ResourceItem.CreateResourceItem(items, Sidvall.Security.ResourceItemAccessLevel.None, "<br/>", true);
        }

        #endregion
        #region LoadAsync

        public virtual async Task LoadAsync()
        {
            var resourceActions = await GetResourceItemsAsync();
            this.Resources = (from o in resourceActions
                              orderby o.Resource
                              select o.Resource).Distinct().ToList();
            this.Actions = (from o in resourceActions
                            orderby o.Action
                            select o.Action).Distinct().ToList();
            this.ResourceItems = await Sidvall.SystemContext.Current.AuthorizationManager.GetSystemResourceItemsAsync();
            this.StoreResourceItems = new List<StoreResourceItem>();
            foreach (var store in Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores)
            {
                var authorizationManager = store.GetAuthorizationManager();
                var storeResourceItem = new StoreResourceItem()
                {
                    ResourceItems = await authorizationManager.GetSystemResourceItemsAsync(),
                    Store = store,
                };
                this.StoreResourceItems.Add(storeResourceItem);
            }
            LoadResults();
        }

        #endregion
        #region LoadResults

        public virtual void LoadResults()
        {
            this.ResourceResults = null;
            var results = new ResourceResultCollection();
            foreach (var resource in this.Resources)
            {
                var resourceResult = results.GetOrCreate(resource);
                foreach (var action in this.Actions)
                {
                    var result = GetResult(resource, action);
                    resourceResult.ResourceActionResults.Add(action, result);
                }
            }
            this.ResourceResults = results;
        }

        #endregion
        #region ShowAction

        public virtual bool ShowAction(string action)
        {
            if (this.ResourceResults != null)
            {
                foreach (var resource in this.Resources)
                {
                    var item = this.ResourceResults.GetResourceItem(resource, action);
                    if (item.AccessLevel != ResourceItemAccessLevel.None)
                        return true;
                }
                return false;
            }
            else
            {
                return true;
            }
        }

        #endregion
        #region ShowResource

        public virtual bool ShowResource(string resource)
        {
            List<ResourceItem> resourceItems;

            if (this.ResourceResults != null)
            {
                foreach (var action in this.Actions)
                {
                    var item = this.ResourceResults.GetResourceItem(resource, action);
                    if (item.AccessLevel != ResourceItemAccessLevel.None)
                        return true;
                }
                return false;
            }
            else
            {
                var items = new ResourceItemCollection();
                switch (resource)
                {
                    case Sidvall.IO.Security.DomainObjects.File:
                    case Sidvall.IO.Security.DomainObjects.FileSystemEntry:
                    case Sidvall.IO.Security.DomainObjects.Folder:
                        foreach (var storeResourceItem in this.StoreResourceItems)
                        {
                            resourceItems = GetResourceItems(storeResourceItem.ResourceItems, resource);
                            items.AddRange(resourceItems);
                        }
                        break;
                    default:
                        resourceItems = GetResourceItems(this.ResourceItems, resource);
                        items.AddRange(resourceItems);
                        break;
                }
                var accessLevel = items.GetHighestAccessLevel();
                if (accessLevel == ResourceItemAccessLevel.None)
                    return false;
                return true;
            }
        }

        #endregion

        #endregion
        #region Private Members

        #region GetResourceItems

        private static List<ResourceItem> GetResourceItems(Sidvall.Security.ResourceItemCollection resourceItems, string resource)
        {
            var items = resourceItems.FindItems(resource).ToList();
            if (items.Count > 1)
            {
                var iMax = items.Count;
                for (int i = iMax - 1; i >= 0; i--)
                {
                    var item = items[i];
                    if (!item.Resource.Equals(resource, System.StringComparison.OrdinalIgnoreCase))
                        items.Remove(item);
                }
            }
            return items;
        }
        private static List<ResourceItem> GetResourceItems(Sidvall.Security.ResourceItemCollection resourceItems, string resource, string action)
        {
            var items = resourceItems.FindItems(resource, action).ToList();
            if (items.Count > 1)
            {
                var iMax = items.Count;
                for (int i = iMax - 1; i >= 0; i--)
                {
                    var item = items[i];
                    if ((!item.Resource.Equals(resource, System.StringComparison.OrdinalIgnoreCase)) ||
                        (!item.Action.Equals(action, System.StringComparison.OrdinalIgnoreCase)))
                    {
                        items.Remove(item);
                    }
                }
            }
            return items;
        }
        private static List<ResourceItem> GetResourceItems(List<StoreResourceItem> storeResourceItems, string resource, string action)
        {
            var items = new List<ResourceItem>();
            foreach (var storeResourceItem in storeResourceItems)
            {
                var resourceItems = GetResourceItems(storeResourceItem.ResourceItems, resource, action);
                foreach (var resourceItem in resourceItems)
                    resourceItem.SubAction = storeResourceItem.Store.RootPath;
                items.AddRange(resourceItems);
            }
            return items;
        }

        #endregion

        #endregion
        #region Constructors

        protected WebApiModelBase()
        {
        }

        #endregion
    }
}
